package keter.framework;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import keter.framework.util.IOUtil;
import keter.framework.util.JdbcUtil;
import keter.framework.util.PathUtil;
import keter.jfinal.KeterAutoTableBindPlugin;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;

import com.google.common.base.Strings;
import com.jfinal.ext.plugin.tablebind.SimpleNameStyles;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.plugin.ehcache.EhCachePlugin;
import com.mysql.jdbc.Connection;

/**
 * <p>
 * Class : app.ConfigLoader
 * <p>
 * Descdription: 系统配置文件初始化
 * 
 * @author 顾力行-gulixing@msn.com
 * @version 1.0.0
 */
public abstract class AbstractBaseConfig extends HttpServlet {
    /**
     * Logger for this class
     */
    private static final Logger logger = LoggerFactory.getLogger(AbstractBaseConfig.class);

    private static String dbUrl;
    private static DruidPlugin druid;
    private static KeterAutoTableBindPlugin katbp;
    private static EhCachePlugin ehcache;
    
    /**Prop prop: 单独使用PropKit生成的prop对象，避免全局PropKit被其他调用“污染”*/
    private static Prop prop;
    /**
     * <p>Method ：useConfig
     * <p>Description : 指定配置文件
     * @param configUrl 
     * @author  gulixing@msn.com
     * @version 1.0.0
     */
    protected static void useConfig(String configUrl) { 
    	String configFileExt = PathUtil.getJarPath()+File.separator+configUrl;
    	if(IOUtil.existsFile(configFileExt)){
        	System.out.println("使用配置文件["+configFileExt+"]初始化应用...");
        	prop = PropKit.use(new File(configFileExt));
		}
    	else{
    		if(IOUtil.existsClassPathFile(configUrl)){
            	prop = PropKit.use(configUrl);
            	System.out.println("使用配置文件[classpath:"+configUrl+"]初始化应用...");
        	}
    		//使用当前目录下的配置文件，用于从jar包启动应用等场景
    		else{
    			throw new RuntimeException("未发现配置文件:"
    		       + PathKit.getRootClassPath()+File.separator+configUrl+","
    		       + configFileExt);			
    		}
    	}
    
     }    	
    
    /**
     * <p>Method ：useDB
     * <p>Description : 指定数据源
     * @param dbUrl 
     * @author  gulixing@msn.com
     * @version 1.0.0
     */
    protected static void useDB(String dbUrl) {
    	AbstractBaseConfig.dbUrl = dbUrl;
    }
  
    /**
     * <p>
     * Method ：getDBUrl
     * <p>
     * Description : 如果子类未显示调用useDB，则使用默认数据源
     *
     * @return
     * @author 顾力行-gulixing@msn.com
     */
    private static String getDBUrl() {
//    	return classpathTranslator(dbUrl);
    	return Strings.isNullOrEmpty(dbUrl)?
    			classpathTranslator(prop.get("db.url")):
    			dbUrl;
    }
    
    /**
     * 处理url中的classpsth:转换成绝对路径
     * @param path
     * @return
     */
    private static String classpathTranslator(String path){
    	String absoluteClassPath = PathKit.getRootClassPath()+File.separator;
    	return path.replace("classpath:", absoluteClassPath);
    }
    
    /**
     * <p>
     * Method ：init
     * <p>
     * Description : servlet启动时执行该方法
     *
     * @param config
     * @throws ServletException
     * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        initAll();
    }
   
    protected void initAll() {
        System.out.println("initing...");
        if (Strings.isNullOrEmpty(prop.get("cache"))){
            logger.warn("未配置缓存(cache)属性，系统缓存功能将不可用！");
        }
        else{
            logger.info("初始化缓存...");
            initCache();
        }
        logger.info("初始化数据源...");
        initDruid();
        logger.info("初始化active record...");
        initActiveRecord();
        // 一旦arp启动之后就不能再添加映射
        // 因此为了照顾单体测试而提出start方法。
        System.out.println("系统初始化完成！");
        katbp.start();
    }

	public void initCache() {
		Resource resource = new DefaultResourceLoader().getResource(prop.get("cache"));
		if (!resource.exists()) {
			logger.warn("缓存配置文件[{}]不存在，缓存功能将不可用！",resource.getDescription());
			return;
		} else {
			try {
				ehcache = new EhCachePlugin(resource.getInputStream());
			} catch (IOException e) {
				logger.error("ehcache配置文件加载失败！", e);
			}
			ehcache.start();
		}
	}

    /**
     * <p>
     * Method ：initActiveRecord
     * <p>
     * Description : 初始化active record: 必须在初始化数据源之后执行
     * 
     * @author gulixing@msn.com
     * @version 1.0.0
     */
	protected KeterAutoTableBindPlugin initActiveRecord() {
        katbp = new KeterAutoTableBindPlugin(
            // 生成唯一arp配置名，解决嵌入式server重启或JUnit批量执行导致配置文件同名问题
        	UUID.randomUUID().toString(), druid, SimpleNameStyles.LOWER
        );
        //.includeAllJarsInLib(true);
        // 不区分大小写
        katbp.setContainerFactory(new CaseInsensitiveContainerFactory(true));
        addPathForUnitTest(katbp);
        addKeterModel(katbp);
        if (getDBUrl().contains("sqlite")) {
            // 对sqlite设定专门的事务层级
            katbp.setTransactionLevel(Connection.TRANSACTION_READ_UNCOMMITTED);
            katbp.setDialect(new Sqlite3Dialect());
        }
        return katbp;
    }
    
    
	 /**
	 * 识别Keter所在的类路径，使派生应用的atbp可以扫描到框架的model
	 * 如果得到的是jar包则加入jar包，否则加入class所在目录
	 * eclipse环境 --> [project_home]\keter\keter-framework\target\classes\keter\framework\
	 * maven环境 --> 
	 * [project_home]/keter/keter-app/file:[repository_home]/org/keter-framework/0.8-SNAPSHOT/keter-framework-0.8-SNAPSHOT.jar!/keter/framework
       Executable War：
       jetty-console(win) --> 
       [win_temp]\file:\[win_temp]\keter-app-jetty.jar_8080\webapp\WEB-INF\lib\keter-framework-0.8-SNAPSHOT.jar!\keter\framework
       jetty-console(linux) --> 
       /tmp/keter-app-jetty.jar_8080/webapp/WEB-INF/lib/keter-framework-0.8-SNAPSHOT.jar
	 * @param katbp 
	 * @author  gulx@neusoft.com
	 * @date    2016年1月12日
	 */
	private void addKeterModel(KeterAutoTableBindPlugin katbp) {
		 //windows get:
	     String me = PathKit.getPath(AbstractBaseConfig.class);
		 logger.info("========老子在这里:\n"+me);
		 String jarpath;
	     //运行环境包含jar包(用于windows、linux直接运行maven环境以及执行war包等)
	     if(me.contains(".jar!") && me.contains("file:")){   	 
	    	 jarpath = StringUtils.substringBetween(me,"file:","!");
	    	 //将jar包加入扫描路径
	    	 katbp.addScanJars(jarpath);
	      }	    	 
	     else{
	    	 //将目录[project_path]\keter\keter-framework\target\classes\加入扫描路径
		     katbp.addScanPackages(StringUtils.substringBeforeLast(me, "keter"));
		     logger.info("=======add path:["+StringUtils.substringBeforeLast(me, "keter")+"]");
	     }
	 }

    /**
     * <p>
     * Method ：setTestPath
     * <p>
     * Description : 补充对maven单体测试环境类路径的支持
     * "target/test-classes" -> "target/classes"
     * @param katbp
     * @author 顾力行-gulixing@msn.com
     */
    private void addPathForUnitTest(KeterAutoTableBindPlugin katbp) {
        String rp = PathKit.getRootClassPath();// target\test-classes
        if (rp.contains("test-classes")) {
        	katbp.addScanPackages(StringUtils.substringBefore(rp, "test-classes")+"classes");
        } 
    }

    public static DruidPlugin initDruid() {
    	dbUrl = classpathTranslator(getDBUrl());
        if (JdbcUtil.getDBType(dbUrl).equals(JdbcUtil.DBType.MYSQL)) {
            druid = new DruidPlugin(dbUrl, prop.get("db.user"), prop.get("db.password"));
        } else if (JdbcUtil.getDBType(dbUrl).equals(JdbcUtil.DBType.SQLITE)) {
        	logger.info("sqlite数据库位置:{}",dbUrl);
            druid = new DruidPlugin(dbUrl, "", "");
        }
        // 选择性传递druid参数，兼容单体测试
        if (prop.containsKey("pool.initialSize")) {
            druid.setInitialSize(prop.getInt("pool.initialSize"));
        }
        if (prop.containsKey("pool.minIdle")) {
            druid.setMinIdle(prop.getInt("pool.minIdle"));
        }
        if (prop.containsKey("pool.maxActive")) {
            druid.setMaxActive(prop.getInt("pool.maxActive"));
        }
        if (!druid.start())
            druid.start();
        return druid;
    }
}